package game.entities.enemies;

import game.CollisionChecker;
import game.entities.IEnemyEntity;

import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.newdawn.slick.font.effects.ConfigurableEffect;

import application.Configuration;
import application.messages.commands.parameters.Coordinates2D;

/**
 * Cares about all enemies in the game. It's responsible for its
 * moving, redrawing etc.
 * 
 * @author Tomas Zima
 */
public class EnemiesManager implements Serializable
{
	/**
	 * Constructor.
	 * Saves instance of collision checker to make the class able
	 * to add enemies into that.
	 * 
	 * @param collisionChecker
	 */
	public EnemiesManager(CollisionChecker collisionChecker)
	{
		this.collisionChecker = collisionChecker;
	}
	
	/**
	 * Adds enemy into list of enemies and into collision checker
	 * as well.
	 * 
	 * @param enemy
	 */
	public void addEnemy(IEnemyEntity enemy)
	{
		this.entities.add(enemy);
		this.collisionChecker.addCollidableObject(enemy);
	}
	
	/**
	 * Update all enemies in the scene.
	 * 
	 * @param deltaTime
	 */
	public void update(long deltaTime, int playerX, int playerY)
	{
		for (int i = 0; i < entities.size(); i++)
		{
			// Update enemies (movement)
			entities.get(i).update(deltaTime, playerX, playerY);
	
			/*
			 * Remove all enemies which were destroyed. 
			 */
			if (entities.get(i).isDestroyed())
			{
				collisionChecker.removeGeneralEnemy(entities.get(i));
				entities.remove(i);
				i--;
			}
			
			/*
			 * Remove all enemies which are too far from the player.
			 */
			try
			{
				// Check
				if (Math.abs(entities.get(i).getPosition().x - playerX) > maxPlayerDistance ||
					Math.abs(entities.get(i).getPosition().y - playerY) > maxPlayerDistance)
				{
					// Delete enemy
					collisionChecker.removeGeneralEnemy(entities.get(i));
					entities.remove(i);
					i--;
				}
			}
			catch (ArrayIndexOutOfBoundsException errHandler)
			{
				//
			}
			catch (IndexOutOfBoundsException errHandler)
			{
				//
			}
		}
		
		if (entities.size() < enemiesMinimalCount)
		{
			if (this.startTime + 15000 < System.currentTimeMillis())
			{
				spawner(enemiesMinimalCount - entities.size());
			}
		}
	}
	
	/**
	 * Redraw all enemies.
	 * 
	 * @param cameraX
	 * @param cameraY
	 */
	public void redraw(int cameraX, int cameraY)
	{
		pushLastPosition(cameraX, cameraY);
		
		for (int i = 0; i < entities.size(); i++)
		{
			entities.get(i).redraw(cameraX, cameraY);
		}
	}
	
	/**
	 * Spawn defined count of enemies.
	 * 
	 * @param count
	 */
	public void spawner(int count)
	{
		// quick & dirty hack
		if (!Configuration.ENABLE_ENEMIES) {
			return;
		}
		
		for (int i = 0; i < count; i++)
		{
			/*int x = randomizer.nextInt(30) + 25;
			//int x = 35 * 32;
			int y = 500 * 32;
			
			addNewEnemy(x, y);*/
			
			// Default (best suiting) position
			int x = lastPositionX;
			int y = lastPositionY - 15 * Configuration.BLOCK_SIZE;

			// Variance of position
			int t1 = randomizer.nextInt(this.maxPlacementVariance);
			int t2 = randomizer.nextInt(this.maxPlacementVariance);
			
			// Way of (position) movement
			boolean b1 = randomizer.nextBoolean();
			boolean b2 = randomizer.nextBoolean();
			
			/*
			 * Movement in the X axis.
			 */
			if (b1)
			{
				x += t1;
			}
			else
			{
				x-= t1;
			}
			
			/*
			 * Movement in the Y axis. 
			 */
			if (b2)
			{
				y += t2;
			}
			else
			{
				y -= t2;
			}

			/*
			 * Find free place to place the enemy. 
			 */
			Coordinates2D pos = collisionChecker.getFreeSpace(
					x / Configuration.BLOCK_SIZE, 
					y / Configuration.BLOCK_SIZE, 
					2, 5);

			/*
			 * If free place was found...
			 */
			if (pos != null)
			{
				// Add new enemy
				addNewEnemy(
						pos.x * Configuration.BLOCK_SIZE,
						pos.y * Configuration.BLOCK_SIZE);
			
				System.out.println("Spawnut bubak na: " + pos.x + ": " + pos.y);
			}
		}
	}

	/**
	 * Create new enemy on defined position.
	 * 
	 * @param x
	 * @param y
	 */
	private void addNewEnemy(int x, int y)
	{
		GeneralEnemy enemy = new GeneralEnemy(x, y);
		entities.add(enemy);
		collisionChecker.addCollidableObject(enemy);
	}
	
	/**
	 * Save last position of camera.
	 * 
	 * @param cameraX
	 * @param cameraY
	 */
	private final void pushLastPosition(int cameraX, int cameraY)
	{
		this.lastPositionX = cameraX;
		this.lastPositionY = cameraY;
	}
	
	/**
	 * Kill all enemies.
	 * Remember, that there will be spawn new enemies immediately, as
	 * this not stop spawning.
	 */
	public void killAllEnemies()
	{
		for (int i = 0; i < entities.size(); i++)
		{
			collisionChecker.removeGeneralEnemy(entities.get(i));
			entities.remove(i);
			
			i--;
		}
	}

	// Store place of all enemies 
	private List<IEnemyEntity>	entities = new LinkedList<IEnemyEntity>();
	
	// EnemiesManager needs raw access to CollisionChecker (adding
	// of a new enemies and deleting them).
	private CollisionChecker	collisionChecker;
	
	// Randomizer
	private Random				randomizer = new Random();
	
	// Minimal count of enemies which will be in the game.
	private int					enemiesMinimalCount	= 10;
	
	// Last position of camera (player). It's used
	// to let spawner know where to spawn.
	private int					lastPositionX;
	private int					lastPositionY;
	
	// Maximal distance from the player in pixels
	private int					maxPlayerDistance = 2500;
	
	// Maximal variance for placing
	// (from this position, nearest free place is searched).
	private int					maxPlacementVariance = 200;
	
	// Time when instance was created. It's necessary to be able
	// to create a "spawn lag" at the beginning (because spawning
	// from the start of game wouldn't be nice).
	private long				startTime = System.currentTimeMillis();
}
